package actor

import (
	"fmt"
	"sync"
	"time"
)

type VFOnStarted func(ctx IContext)
type VFOnStopping func(ctx IContext)
type VFOnStopped func(ctx IContext)
type VFOnReceiveMessage func(message interface{}, sender *PID, ctx IContext)
type VFOnActorTerminated func(who *PID, ctx IContext)
type VFOnRestarting func(ctx IContext)
type VFOnRestarted func(ctx IContext)

type Super struct {
	vfOnStarted         VFOnStarted
	vfOnStopping        VFOnStopping
	vfOnStopped         VFOnStopped
	vfOnReceiveMessage  VFOnReceiveMessage
	vfOnActorTerminated VFOnActorTerminated
	vfOnRestarting      VFOnRestarting
	vfOnRestarted       VFOnRestarted

	PID       *PID
	ParentPID *PID
	timerMgr  *TimerManager

	stoppedWg *sync.WaitGroup
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func NewSuper(
	vfOnStarted VFOnStarted,
	vfOnStopping VFOnStopping,
	vfOnStopped VFOnStopped,
	vfOnReceiveMessage VFOnReceiveMessage,
	vfOnActorTerminated VFOnActorTerminated,
	vfOnRestarting VFOnRestarting,
	vfOnRestarted VFOnRestarted,
	stoppedWg *sync.WaitGroup,
) *Super {

	s := &Super{
		vfOnStarted:         vfOnStarted,
		vfOnStopping:        vfOnStopping,
		vfOnStopped:         vfOnStopped,
		vfOnReceiveMessage:  vfOnReceiveMessage,
		vfOnActorTerminated: vfOnActorTerminated,
		vfOnRestarting:      vfOnRestarting,
		vfOnRestarted:       vfOnRestarted,
		stoppedWg:           stoppedWg,
	}

	if s.vfOnStarted == nil {
		s.vfOnStarted = func(ctx IContext) {}
	}
	if s.vfOnStopping == nil {
		s.vfOnStopping = func(ctx IContext) {}
	}
	if s.vfOnStopped == nil {
		s.vfOnStopped = func(ctx IContext) {}
	}
	if s.vfOnReceiveMessage == nil {
		s.vfOnReceiveMessage = func(message interface{}, sender *PID, ctx IContext) {}
	}
	if s.vfOnActorTerminated == nil {
		s.vfOnActorTerminated = func(who *PID, ctx IContext) {}
	}
	if s.vfOnRestarting == nil {
		s.vfOnRestarting = func(ctx IContext) {}
	}
	if s.vfOnRestarted == nil {
		s.vfOnRestarted = func(ctx IContext) {}
	}

	return s
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 子类不要覆盖OnReceiveMessage
func (s *Super) OnReceiveMessage(message interface{}, sender *PID, ctx IContext) {
	switch msg := message.(type) {
	case *Started:
		s.PID = ctx.Self()
		s.ParentPID = ctx.Parent()
		s.timerMgr = NewTimerManager()
		s.timerMgr.pid = s.PID
		if s.stoppedWg != nil {
			s.stoppedWg.Add(1)
		}
		s.vfOnStarted(ctx)
		s.Debug("started")

	case *Stopping:
		s.Debug("stopping")
		s.timerMgr.StopAll()
		s.vfOnStopping(ctx)

	case *Stopped:
		s.vfOnStopped(ctx)
		s.Debug("stopped")
		if s.stoppedWg != nil {
			s.stoppedWg.Done()
		}

	case *Terminated:
		s.Debug("[%s] terminated", msg.Who.Name)
		s.vfOnActorTerminated(msg.Who, ctx)

	case *Restarting:
		s.Error("restarting")
		s.vfOnRestarted(ctx)

	case *Restarted:
		s.Error("restarted")
		s.vfOnRestarting(ctx)

	case *Timeout:
		if err := s.timerMgr.Trigger(msg.ID); err != nil {
			logger.Error("[%s] trigger time fail, %s", s.PID.Name, err)
		}

	default:
		s.vfOnReceiveMessage(message, sender, ctx)
	}
}

////////////////////////////////////////////////////////////////////////////////
func (s *Super) Debug(format string, args ...interface{}) {
	logger.Debug("[%s] %s", s.PID.Name, fmt.Sprintf(format, args...))
}

func (s *Super) Info(format string, args ...interface{}) {
	logger.Info("[%s] %s", s.PID.Name, fmt.Sprintf(format, args...))
}

func (s *Super) Warn(format string, args ...interface{}) {
	logger.Warn("[%]s %s", s.PID.Name, fmt.Sprintf(format, args...))
}

func (s *Super) Error(format string, args ...interface{}) {
	logger.Error("[%s] %s", s.PID.Name, fmt.Sprintf(format, args...))
}

func (s *Super) Emergency(format string, args ...interface{}) {
	logger.Emergency("[%s] %s", s.PID.Name, fmt.Sprintf(format, args...))
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (s *Super) NewTimer(dur time.Duration, cb TimerCallback) TimerID {
	return s.timerMgr.NewTimer(dur, cb)
}

func (s *Super) NewLoopTimer(interval time.Duration, cb TimerCallback) TimerID {
	return s.timerMgr.NewLoopTimer(interval, cb)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
